/*
 *  $TEAM_NAME Space Trader
 */
package models;

import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.LinkedList;
import java.util.Observable;

/**
 * The ship represents a ship within the game
 * @author bradley
 * @version 1.0
 */
public class Ship extends Observable {
	/**
	 * field
	 */
	private String name;
	
	/**
	 * field
	 */
	private int weapon_slots;
	
	/**
	 * field
	 */
	private int shield_slots;
	
	/**
	 * field
	 */
	private int gadget_slots;
	
	/**
	 * field
	 */
	private int cargo_slots;

	/**
	 * field
	 */
	private int crew_size;
	
	/**
	 * field
	 */
	private int hull_strength;
	
	/**
	 * field
	 */
	private int fuel_cost;
	
	/**
	 * field
	 */
	private int price;
	
	/**
	 * field
	 */
	private TechLevel min_tech;

	/**
	 * field
	 */
	private List<Weapon> weapons;
	
	/**
	 * field
	 */
	private List<Gadget> gadgets;
	
	/**
	 * field
	 */
	private List<Shield> shields;
	
	/**
	 * field
	 */
	private List<Crew> crew;
	
	/**
	 * field
	 */
	private Map<String, Integer> hold;

	/**
	 * field
	 */
	private int currentHullStrength;
	
	/**
	 * field
	 */
	private FuelTank fuel;
	
	/**
	 * field
	 */
	private Health health;
	
	/**
	 * field
	 */
	private static final double SCALE_VALUE =.01;
	
	/**
	 * field
	 */
	private static final int  SCALE_FUEL = 100;
	
	/**
	 * field
	 */
	private static final Ship FLEA_MODEL = new Ship("Flea", 
			10, 0, 0, 0, 1, 45, 25, 1, 2000, 
			TechLevel.PRE_AGRICULTURE);
	
	/**
	 * field
	 */
	private static final Ship GNAT_MODEL =  new Ship("Gnat", 
			15, 1, 0, 1, 1, 14, 100, 2, 10000, 
			TechLevel.AGRICULTURE);
	
	/**
	 * field
	 */
	private static final Ship FIREFLY_MODEL = new Ship("FireFly", 
			20, 1, 1, 1, 1, 17, 100, 3, 25000, 
			TechLevel.EARLY_INDUSTRIAL);
	
	/**
	 * field
	 */
	private static final Ship MOSQUITO_MODEL = new Ship("Mosquito", 
			15, 2, 1, 1, 1, 13, 100, 5, 30000, 
			TechLevel.INDUSTRIAL);
	
	/**
	 * field
	 */
	private static final Ship BUMBLEBEE_MODEL = new Ship("Bumblebee", 
			25, 1, 2, 2, 2, 15, 100, 7, 60000, 
			TechLevel.INDUSTRIAL);
	
	/**
	 * no arg constructor for ship
	 */
	public Ship(){
		this.name = "";
	}
	
	/**
	 * Constructor for ship
	 * @param ship The ship
	 */
	public Ship(Ship ship){
		this(ship.name, ship.cargo_slots, ship.weapon_slots, ship.shield_slots,
				ship.gadget_slots, ship.crew_size, ship.fuel.getMaxFuel(), 
				ship.hull_strength, ship.fuel_cost, ship.price, ship.min_tech);
	}
	
	/**
	 * Creates a new ship with specific arguments
	 * @param name - the name of the ship
	 * @param cargoSlots - the # of cargo slots
	 * @param weaponSlots - the # of weapon slots
	 * @param shieldSlots - the # of shield slots
	 * @param gadgetSlots - the # of gadget slots
	 * @param crewSize - the max # of crew
	 * @param maxFuel - the max amount of fuel
	 * @param hullStrength - the hull strength
	 * @param fuelCost - the fuel cost
	 * @param price - the price of the ship
	 * @param minTech - the min tech level required to sell
	 */
	public Ship(String name, int cargoSlots, int weaponSlots, 
			int shieldSlots, int gadgetSlots, 
			int crewSize, int maxFuel, int hullStrength, 
			int fuelCost, int price, TechLevel minTech) {
		this.name = name;
		
		this.weapon_slots = weaponSlots;
		this.cargo_slots = cargoSlots;
		this.shield_slots = shieldSlots;
		this.gadget_slots = gadgetSlots;
		this.crew_size = crewSize;
		this.hull_strength = hullStrength;
		this.fuel_cost = fuelCost;
		this.price = price;
		this.min_tech = minTech;
		
		this.fuel = new FuelTank(maxFuel);
		this.currentHullStrength = this.hull_strength;
		this.health = new Health(this.hull_strength);

		this.weapons = new ArrayList<Weapon>();
		this.gadgets = new ArrayList<Gadget>();
		this.shields = new ArrayList<Shield>();
		this.crew = new ArrayList<Crew>();
		this.hold = new HashMap<String, Integer>();

        for (TradeGood good : TradeGood.getTradeGoods()) {
            this.hold.put(good.getName(), 0);
        }
	}
	
	/**
	 * Adds a weapon to a ship
	 * @param weapon - the weapon to add
	 * @return if it was added
	 */
	public boolean canAddWeapon(Weapon weapon) {
		if(this.weapons.size() < weapon_slots){
			this.weapons.add(weapon);
			return true;
		}
		return false;
	}
	
	/**
	 * Refuels the fuel tank and returns the untis of fuel used
	 * @return the units of fuel used
	 */
	public int refuel() {
		return this.fuel.fillTank();
	}
	
	/** 
	 * Gets the range of the ship
	 * @return the range 
	 */
	public double getRange() {
        // Convert between tons of fuel and (Galactic Units?)
        return (double) fuel.getCurrentFuel() * SCALE_VALUE;
    }
	
	/**
	 * Gets the price of the ship
	 * @return the price
	 */
	public int getPrice() {
		return price;
	}
	
	/**
	 * Gets the min tech level
	 * @return the min tech level
	 */
	public TechLevel getMinTechLevel() {
		return min_tech;
	}
	
	/**
	 * Uses fuel on a ship
	 * @param units the units used
	 */
	public void useFuel(double units) {
		this.fuel.useFuel((int) (units * SCALE_FUEL));
	}
	
	/**
	 * Adds a tradegood to the cargo hold
	 * 
	 * @param good The good to be added
	 * @param quantity
	 * @return true if added, false if already at capacity
	 */
	public boolean canAddTradeGood(TradeGood good, int quantity) {
        if (quantity > getRemainingCargoCapacity()) {
            return false;
        }

        final int prev = getTradeGoodQuantity(good);  
        hold.put(good.getName(), prev + quantity);

        setChanged();
        return true;
	}

	/**
	 * Removes a trade good from the cargo hold
	 * @param good - the good to remove
	 * @param quantity - the quantity to remove
	 * @return if the remove was successful
	 */
	public boolean canRemoveTradeGood(TradeGood good, int quantity) {
        final int prev = getTradeGoodQuantity(good);
        final int after = prev - quantity;

        if (after < 0) {
            return false;
        }

        hold.put(good.getName(), after);

        setChanged();
        return true;
    }

	/**
	 * Gets how many of a trade good the ship has
	 * @param good the trade good
	 * @return the quantity
	 */
	public int getTradeGoodQuantity(TradeGood good) {
	    return hold.get(good.getName());
	}

	/**
	 * Gets the remaining capactiy
	 * @return the capacity
	 */
	public int getRemainingCargoCapacity() {
        return getMaxCargoCapacity() - getUsedCargoCapacity();
	}

	/**
	 * Getter for the fuel tank
	 * @return the fuel tank
	 */
	public FuelTank getFuelTank() {
        return fuel;
    }
	
	/**
	 * Getter for the name
	 * @return the name
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * Gets the used capacity
	 * @return the used capacity
	 */
	public int getUsedCargoCapacity() {
        int used = 0;

        for (TradeGood good : TradeGood.getTradeGoods()) 
            used += hold.get(good.getName());
       
        return used;
    }
	
	/**
	 * Getter for the max capacity
	 * @return the max capacity
	 */
	public int getMaxCargoCapacity() {
		return cargo_slots;
	}
	
	/**
	 * Gets the type of ships available
	 * @return the types of ships
	 */
	public static Collection<Ship> getShips() {
		final Ship flea = flea();
		final Ship gnat = gnat();
		final Ship firefly = fireFly();
		final Ship mosquito = mosquito();
		final Ship bumblebee = bumblebee();
		
		final Collection<Ship> ships = new LinkedList<Ship>();
		
		ships.add(flea);
		ships.add(gnat);
		ships.add(firefly);
		ships.add(mosquito);
		ships.add(bumblebee);
		
		return ships;
	}
	
	/**
	 * Finds if the ship is for sale in a solar system
	 * @param ss the solar system
	 * @return if it is for sale
	 */
	public Boolean getAvailable(StarSystem ss){
		return getAvailableTech(ss.getTechLevel());
	}
	
	/**
	 * Finds if the ship is for sale based on the tech level
	 * @param techLevel the tech level
	 * @return if it is for sale
	 */
	public Boolean getAvailableTech(TechLevel techLevel) {
		if(this.getMinTechLevel().ordinal() <= techLevel.ordinal()){
			return true;
		}
		return false;
	}
	
	/**
	 * Gets a new instance of the flea ship
	 * @return flea ship
	 */
	public static Ship flea() {
		return new Ship(Ship.FLEA_MODEL);
	}
	
	/**
	 * Gets a new instance of the gnat ship
	 * @return gnat ship
	 */
	public static Ship gnat() {
		return new Ship(Ship.GNAT_MODEL);
	}
	
	/**
	 * Gets a new instance of the fireFly ship
	 * @return the firefly ship
	 */
	public static Ship fireFly() {
		return new Ship(Ship.FIREFLY_MODEL);
	}
	
	/**
	 * Gets a new instance of the mosquito ship
	 * @return the mosquito ship
	 */
	public static Ship mosquito() {
		return new Ship(Ship.MOSQUITO_MODEL);
	}
	
	/**
	 * Gets a new instance of the bumblebee ship
	 * @return the bumblebee ship
	 */
	public static Ship bumblebee() {
		return new Ship(Ship.BUMBLEBEE_MODEL);
    }
	
	//Getters and Setters
	/**
	 * Gets the weapon slots
	 * @return the weapon slots
	 */
	public int getWEAPON_SLOTS() {
		return weapon_slots;
	}

	/**
	 * Setter for weapon slots
	 * @param wEAPON_SLOTS the weapon slots
	 */
	public void setWEAPON_SLOTS(int wEAPON_SLOTS) {
		weapon_slots = wEAPON_SLOTS;
	}

	/**
	 * Gets the sheild slots
	 * @return the shield slots
	 */
	public int getSHIELD_SLOTS() {
		return shield_slots;
	}

	/**
	 * Setter for the shield slots
	 * @param sHIELD_SLOTS the shiled slots
	 */
	public void setSHIELD_SLOTS(int sHIELD_SLOTS) {
		shield_slots = sHIELD_SLOTS;
	}

	/**
	 * Getter for the gadget slots
	 * @return the gadget slots
	 */
	public int getGADGET_SLOTS() {
		return gadget_slots;
	}

	/**
	 * Setter for the gadget slots
	 * @param gADGET_SLOTS the gadget slots
	 */
	public void setGADGET_SLOTS(int gADGET_SLOTS) {
		gadget_slots = gADGET_SLOTS;
	}

	/**
	 * Getter for the cargo slots
	 * @return the cargo slots 
	 */
	public int getCARGO_SLOTS() {
		return cargo_slots;
	}

	/**
	 * Setter for the cargo slots
	 * @param cARGO_SLOTS - the cargo slots
	 */
	public void setCARGO_SLOTS(int cARGO_SLOTS) {
		cargo_slots = cARGO_SLOTS;
	}

	/**
	 * Getter for the crew size
	 * @return the crew size
	 */
	public int getCREW_SIZE() {
		return crew_size;
	}

	/**
	 * Setter for the crew size
	 * @param cREW_SIZE the crew size
	 */
	public void setCREW_SIZE(int cREW_SIZE) {
		crew_size = cREW_SIZE;
	}

	/**
	 * Getter for the hull strength
	 * @return the hull strength
	 */
	public int getHULL_STRENGTH() {
		return hull_strength;
	}

	/**
	 * setter for the hull strength
	 * @param hULL_STRENGTH the hull strength
	 */
	public void setHULL_STRENGTH(int hULL_STRENGTH) {
		hull_strength = hULL_STRENGTH;
	}

	/**
	 * Getter for the fuel cost
	 * @return the fuel cost
	 */
	public int getFUEL_COST() {
		return fuel_cost;
	}

	/**
	 * Setter for the fuel cost
	 * @param fUEL_COST the fuel cost
	 */
	public void setFUEL_COST(int fUEL_COST) {
		fuel_cost = fUEL_COST;
	}

	/**
	 * Setter for the price
	 * @param pRICE the price
	 */
	public void setPRICE(int pRICE) {
		price = pRICE;
	}

	/**
	 * Getter for the min tech level
	 * @return the tech level
	 */
	public TechLevel getMIN_TECH() {
		return min_tech;
	}

	/**
	 * Get the min tech level
	 * @param mIN_TECH the tech level
	 */
	public void setMIN_TECH(TechLevel mIN_TECH) {
		min_tech = mIN_TECH;
	}

	/**
	 * Getter for the weapons
	 * @return the weapons
	 */
	public List<Weapon> getWeapons() {
		return weapons;
	}

	/**
	 * Setter for the weapons 
	 * @param weapons the weapons
	 */
	public void setWeapons(List<Weapon> weapons) {
		this.weapons = weapons;
	}

	/**
	 * Getter for the gadgets
	 * @return the gadgets
	 */
	public List<Gadget> getGadgets() {
		return gadgets;
	}

	/**
	 * Setter for the gadgets
	 * @param gadgets the gadgets
	 */
	public void setGadgets(List<Gadget> gadgets) {
		this.gadgets = gadgets;
	}

	/**
	 * Getter for the shield
	 * @return the shield
	 */
	public List<Shield> getShields() {
		return shields;
	}

	/**
	 * Getter for the shields
	 * @param shields the shilds
	 */
	public void setShields(List<Shield> shields) {
		this.shields = shields;
	}

	/**
	 * Getter for the crew 
	 * @return the crew
	 */
	public List<Crew> getCrew() {
		return crew;
	}
	
	/**
	 * Setter for the crew
	 * @param crew the crew
	 */
	public void setCrew(List<Crew> crew) {
		this.crew = crew;
	}

	/**
	 * Getter for the hold
	 * @return the hold
	 */
	public Map<String, Integer> getHold() {
		return hold;
	}

	/**
	 * Setter for the hold
	 * @param hold the hold
	 */
	public void setHold(Map<String, Integer> hold) {
		this.hold = hold;
	}

	/**
	 * Getter for the fuel tank
	 * @return the fuel tank
	 */
	public FuelTank getFuel() {
		return fuel;
	}

	/**
	 * Setter for the fuel
	 * @param fuel the fuel tank
	 */
	public void setFuel(FuelTank fuel) {
		this.fuel = fuel;
	}

	/**
	 * Setter for the name
	 * @param name the name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Computes the attack strength of the ship
	 * @return the attack strength
	 */
	public int attack(){
		int strength = 0;
		if(this.crew != null){
			for(Crew c : this.crew){
				strength += c.fighter;
			}
		}
		for(Weapon w : this.weapons){
			strength += w.getDamage();
		}
		return strength;
	}
	
	/**
	 * Defends the ship from the attack
	 * @param attack the attack
	 */
	public void defend(int attack){
		if(this.shields != null){
			for(Shield s : this.shields) {
				int defense = s.getDefense();
				attack -= defense;
			}
		}
		if(this.health.getValue() - attack > 0){
			this.health.setValue(this.health.getValue() - attack);
		} else {
			this.health.setValue(0);
		}
	}

	/**
	 * Getter for the hull strength
	 * @return the hull strength
	 */
	public int getCurrentHullStrength() {
		return currentHullStrength;
	}

	/**
	 * Setter for the hull stregnth
	 * @param currentHullStrength the hull strength
	 */
	public void setCurrentHullStrength(int currentHullStrength) {
		this.currentHullStrength = currentHullStrength;
	}
	
	/**
	 * @return the health
	 */
	public Health getHealth() {
		return health;
	}

	/**
	 * @param health the health to set
	 */
	public void setHealth(Health health) {
		this.health = health;
	}
	
	/**
	 * @return a string describing the object
	 */
	public String toString() {
		return "";
	}
}
